#include "b64.h"


OPEN_JLIB_NS


static const s8 ENCODE_TBL[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static const u8 DECODE_TBL[] =
  "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
  "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
  "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x3E\xFF\xFF\xFF\x3F"
  "\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\xFF\xFF\xFF\x00\xFF\xFF"
  "\xFF\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E"
  "\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\xFF\xFF\xFF\xFF\xFF"
  "\xFF\x1A\x1B\x1C\x1D\x1E\x1F\x20\x21\x22\x23\x24\x25\x26\x27\x28"
  "\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30\x31\x32\x33\xFF\xFF\xFF\xFF\xFF"
  "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
  "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
  "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
  "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
  "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
  "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
  "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
  "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";



uwl B64::encode (void *dst, const void *src, uwl src_size) noexcept {
  auto dp = (s8*)dst;
  auto sp = (const u8*)src;
  auto tr = ENCODE_TBL;
  for (; src_size >= 3; src_size -= 3; sp += 3) {
    auto i0 = sp[0] >> 2;
    auto i1 = ((sp[0] << 4) & 0x30) | (sp[1] >> 4);
    auto i2 = ((sp[1] << 2) & 0x3C) | (sp[2] >> 6);
    auto i3 = sp[2] & 0x3F;
    *dp++ = tr[i0];
    *dp++ = tr[i1];
    *dp++ = tr[i2];
    *dp++ = tr[i3];
  }
  if (src_size == 2) {
    auto i0 = sp[0] >> 2;
    auto i1 = ((sp[0] << 4) & 0x30) | (sp[1] >> 4);
    auto i2 = ((sp[1] << 2) & 0x3C);
    *dp++ = tr[i0];
    *dp++ = tr[i1];
    *dp++ = tr[i2];
    *dp++ = '=';
  }
  else if (src_size == 1) {
    auto i0 = sp[0] >> 2;
    auto i1 = (sp[0] << 4) & 0x30;
    *dp++ = tr[i0];
    *dp++ = tr[i1];
    *dp++ = '=';
    *dp++ = '=';
  }
  return dp - (s8*)dst;
}



swl B64::decode (void *dst, const void *src, uwl src_size) noexcept {
  if (src_size & 3)
    return -1;
  auto dp = (u8*)dst;
  auto sp = (s8*)src;
  auto tr = DECODE_TBL;
  for (; src_size >= 8; src_size -= 4; ) {
    auto i0 = tr[*sp++];
    auto i1 = tr[*sp++];
    auto i2 = tr[*sp++];
    auto i3 = tr[*sp++];
    if (i0 == 0xFF || i1 == 0xFF || i2 == 0xFF || i3 == 0xFF)
      return -1;
    *dp++ = (i0 << 2) | ((i1 >> 4) & 0x03);
    *dp++ = (i1 << 4) | ((i2 >> 2) & 0x0F);
    *dp++ = (i2 << 6) | i3;
  }
  {
    auto i0 = tr[sp[0]];
    auto i1 = tr[sp[1]];
    auto i2 = tr[sp[2]];
    auto i3 = tr[sp[3]];
    if (i0 == 0xFF || i1 == 0xFF || i2 == 0xFF || i3 == 0xFF)
      return -1;
    *dp++ = (i0 << 2) | ((i1 >> 4) & 0x03);
    if (sp[2] != '=') {
      *dp++ = (i1 << 4) | ((i2 >> 2) & 0x0F);
      if (sp[3] != '=')
        *dp++ = (i2 << 6) | i3;
    }
    else if (sp[3] != '=')
      return -1;
  }
  return (s64)(dp - (u8*)dst);
}



uwl B64::decode_unsafe (void *dst, const void *src, uwl src_size) noexcept {
  auto dp = (u8*)dst;
  auto sp = (s8*)src;
  auto tr = DECODE_TBL;
  for (; src_size >= 8; src_size -= 4; ) {
    auto i0 = tr[*sp++];
    auto i1 = tr[*sp++];
    auto i2 = tr[*sp++];
    auto i3 = tr[*sp++];
    *dp++ = (i0 << 2) | ((i1 >> 4) & 0x03);
    *dp++ = (i1 << 4) | ((i2 >> 2) & 0x0F);
    *dp++ = (i2 << 6) | i3;
  }
  {
    auto i0 = tr[sp[0]];
    auto i1 = tr[sp[1]];
    auto i2 = tr[sp[2]];
    auto i3 = tr[sp[3]];
    *dp++ = (i0 << 2) | ((i1 >> 4) & 0x03);
    if (sp[2] != '=') {
      *dp++ = (i1 << 4) | ((i2 >> 2) & 0x0F);
      if (sp[3] != '=')
        *dp++ = (i2 << 6) | i3;
    }
  }
  return (s64)(dp - (u8*)dst);
}


CLOSE_JLIB_NS